package queens;
/**
 * This class implements the logic behind the BDD for the n-queens problem
 * You should implement all the missing methods
 * 
 * @author Stavros Amanatidis
 *
 */
import net.sf.javabdd.*;

public class QueensLogic {
    private int x = 0;
    private int y = 0;
    private int[][] board;
    private BDDFactory fact ;
    private BDD[][] BDDBoard;
    private BDD BDDAll;
   
    public QueensLogic() {
       //constructor
    }
    
    public void initializeGame(int size) {
        this.x = size;
        this.y = size;
        this.board = new int[x][y];
        BuildBDDBoard() ;
        BuildRules();
        InvokeRules();
    }
    
    private void BuildBDDBoard() {
         fact = JFactory.init(10,10);
	     fact.setVarNum(this.x*this.y);
             BDDBoard = new BDD[x][y];
             int VarNum = 0;
             for (int col=0; col < x; col++)
             {
                 for (int row=0; row < y; row++)
                 {
                     BDDBoard[col][row]=fact.ithVar(VarNum);
                     VarNum++;
                 }
             }
    }
   
    private void BuildRules() {

      // Initializing BDDAll	
      BDDAll=fact.one();

      // Rules for one queen in row
      BDD[][] OneQueenInRow;  
          
        for (int row = 0; row < y ; row++)
        {    
        OneQueenInRow = new BDD[x][2];
        OneQueenInRow[x-1][0]= BDDBoard[x-1][row];
                    
        OneQueenInRow[x-1][1]= BDDBoard[x-1][row].not();       
        
        for(int col = x - 2; col > 0; col-- )
        {
            OneQueenInRow[col][0]= (BDDBoard[col][row].and(OneQueenInRow[col+1][1])).
                                    or(BDDBoard[col][row].not().and(OneQueenInRow[col+1][0]));
               
            OneQueenInRow[col][1]= BDDBoard[col][row].not().and(OneQueenInRow[col+1][1]);   
        }
                
        OneQueenInRow[0][0] = (BDDBoard[0][row].and(OneQueenInRow[1][1])).
                                    or(BDDBoard[0][row].not().and(OneQueenInRow[1][0]));  
       
        BDDAll = BDDAll.and(OneQueenInRow[0][0]);
        }
       
        // Rules for one queen in column
        BDD[][] OneQueenInColumn;  
          
        for (int Column = 0; Column < x ; Column++)
        {    
             OneQueenInColumn = new BDD[y][2];
             OneQueenInColumn[y-1][0]= BDDBoard[Column][y-1];
                    
        OneQueenInColumn[y-1][1]= BDDBoard[Column][y-1].not();       
        
        for(int row = y - 2; row > 0; row-- )
        {
            OneQueenInColumn[row][0]= (BDDBoard[Column][row].and(OneQueenInColumn[row+1][1])).
                                    or(BDDBoard[Column][row].not().and(OneQueenInColumn[row+1][0]));
               
            OneQueenInColumn[row][1]= BDDBoard[Column][row].not().and(OneQueenInColumn[row+1][1]);   

        }
                
        OneQueenInColumn[0][0] = (BDDBoard[Column][0].and(OneQueenInColumn[1][1])).
                                    or(BDDBoard[Column][0].not().and(OneQueenInColumn[1][0]));  
       
        BDDAll = BDDAll.and(OneQueenInColumn[0][0]);        
        }

        //rules for  diagonals
        BDD MostOneQueenInFirstDiagonal;  
        BDD MostOneQueenInSecondDiagonal;  
       
        for (int Column = 0; Column < x ; Column++)
        {  
          for(int row = 0; row <y; row++ )
          {
               MostOneQueenInFirstDiagonal=fact.one();
         
               for(int i = 1; i < x; i++) 
               {
                           int c = Column-i;
                           int r = row-i;

                           if(c >= 0 && r >= 0) 
                           { 
                                 MostOneQueenInFirstDiagonal = MostOneQueenInFirstDiagonal.and(
                                           (BDDBoard[Column][row].and(BDDBoard[c][r])).not());
                           }

                           c = Column+i;
                           r = row+i;
                           if(c < x && r < y) 
                           {      
                                   MostOneQueenInFirstDiagonal = MostOneQueenInFirstDiagonal.and( 
                                           (BDDBoard[Column][row].and(BDDBoard[c][r])).not());
                           }
               }
               BDDAll = BDDAll.and(MostOneQueenInFirstDiagonal); 
               MostOneQueenInSecondDiagonal=fact.one();
               for(int i = 1; i < x; i++) 
               {
                       int c = Column+i;
                       int r = row-i;
                       if(c < x && r >= 0) 
                       { 
                            MostOneQueenInSecondDiagonal = MostOneQueenInSecondDiagonal.and( 
                                       (BDDBoard[Column][row].and(BDDBoard[c][r])).not());
                       }
                       c = Column-i;
                       r = row+i;
                       if(c > 0 && r < y) 
                       { 
                              MostOneQueenInSecondDiagonal = MostOneQueenInSecondDiagonal.and(
                                       (BDDBoard[Column][row].and(BDDBoard[c][r])).not());
                       }
               }
               BDDAll = BDDAll.and(MostOneQueenInSecondDiagonal); 
               
           
          }
        }    
    }
    
 private void InvokeRules()
 {
	BDD BDDRestricted;
	 
    for (int c = 0; c < x; c++)
    {
        for (int r = 0; r < y ; r++)
        {
            if (board[c][r] == 0) 
            {
                BDDRestricted = BDDAll.restrict(BDDBoard[c][r]);
                if (BDDRestricted.isZero()) 
                    {board[c][r] = -1;}
                
                BDDRestricted = BDDAll.restrict(BDDBoard[c][r].not());
                if (BDDRestricted.isZero()) 
                    {board[c][r] = 1;}                        
            }
            }
        }
    }         
   
    public int[][] getGameBoard() {
        return board;
    }

    public boolean insertQueen(int column, int row) {

        if (board[column][row] == -1 || board[column][row] == 1) {
            return true;
        }
        
        board[column][row]=1;
        
        BDDAll = BDDAll.restrict (BDDBoard[column][row]);
        
        InvokeRules();
       
        return true;
    }  
}